/******************************************************************************
 * Spine Runtimes License Agreement
 * Last updated April 5, 2025. Replaces all prior versions.
 *
 * Copyright (c) 2013-2025, Esoteric Software LLC
 *
 * Integration of the Spine Runtimes into software or otherwise creating
 * derivative works of the Spine Runtimes is permitted under the terms and
 * conditions of Section 2 of the Spine Editor License Agreement:
 * http://esotericsoftware.com/spine-editor-license
 *
 * Otherwise, it is permitted to integrate the Spine Runtimes into software
 * or otherwise create derivative works of the Spine Runtimes (collectively,
 * "Products"), provided that each user of the Products must obtain their own
 * Spine Editor license and redistribution of the Products in any form must
 * include this license and copyright notice.
 *
 * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
 * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************/

import { BlendMode, Color, Disposable, TimeKeeper } from "@esotericsoftware/spine-core";
import { GLTexture } from "./GLTexture.js";
import { ResizeMode, SceneRenderer } from "./SceneRenderer.js";

let spinnerImage: HTMLImageElement;
let logoImage: HTMLImageElement;
let loaded = 0;

const FADE_IN = 1, FADE_OUT = 1;
const logoWidth = 165, logoHeight = 108, spinnerSize = 163;

export class LoadingScreen implements Disposable {
	private renderer: SceneRenderer;
	private logo: GLTexture | null = null;
	private spinner: GLTexture | null = null;
	private angle = 0;
	private fadeOut = 0;
	private fadeIn = 0;
	private timeKeeper = new TimeKeeper();
	backgroundColor = new Color(0.135, 0.135, 0.135, 1);
	private tempColor = new Color();

	constructor (renderer: SceneRenderer) {
		this.renderer = renderer;

		this.timeKeeper.maxDelta = 9;

		if (!logoImage) {
			let isSafari = navigator.userAgent.indexOf("Safari") > -1; // Thank you Apple Inc.
			let onload = () => loaded++;

			logoImage = new Image();
			logoImage.src = SPINE_LOGO_DATA;
			if (!isSafari) logoImage.crossOrigin = "anonymous";
			logoImage.onload = onload;

			spinnerImage = new Image();
			spinnerImage.src = SPINNER_DATA;
			if (!isSafari) spinnerImage.crossOrigin = "anonymous";
			spinnerImage.onload = onload;
		}
	}
	dispose (): void {
		this.logo?.dispose();
		this.spinner?.dispose();
	}

	draw (complete = false) {
		if (loaded < 2 || (complete && this.fadeOut > FADE_OUT)) return;

		this.timeKeeper.update();
		let a = Math.abs(Math.sin(this.timeKeeper.totalTime + 0.25));
		this.angle -= this.timeKeeper.delta * 200 * (1 + 1.5 * Math.pow(a, 5));

		let tempColor = this.tempColor;
		let renderer = this.renderer;
		let canvas = renderer.canvas;
		let gl = renderer.context.gl;

		renderer.resize(ResizeMode.Expand);
		renderer.camera.position.set(canvas.width / 2, canvas.height / 2, 0);
		renderer.batcher.setBlendMode(BlendMode.Normal, true);

		if (complete) {
			this.fadeOut += this.timeKeeper.delta * (this.timeKeeper.totalTime < 1 ? 2 : 1);
			if (this.fadeOut > FADE_OUT) return;
			tempColor.setFromColor(this.backgroundColor);
			a = 1 - this.fadeOut / FADE_OUT;
			a = 1 - (a - 1) * (a - 1);
			tempColor.a *= a;
			if (tempColor.a > 0) {
				renderer.camera.zoom = 1;
				renderer.begin();
				renderer.quad(true, 0, 0, canvas.width, 0, canvas.width, canvas.height, 0, canvas.height,
					tempColor, tempColor, tempColor, tempColor);
				renderer.end();
			}
		} else {
			this.fadeIn += this.timeKeeper.delta;
			if (this.backgroundColor.a > 0) {
				gl.clearColor(this.backgroundColor.r, this.backgroundColor.g, this.backgroundColor.b, this.backgroundColor.a);
				gl.clear(gl.COLOR_BUFFER_BIT);
			}
			a = 1;
		}
		a *= Math.min(this.fadeIn / FADE_IN, 1);
		tempColor.set(a, a, a, a);

		if (!this.logo) {
			this.logo = new GLTexture(renderer.context, logoImage);
			this.spinner = new GLTexture(renderer.context, spinnerImage);
		}
		renderer.camera.zoom = Math.max(1, spinnerSize / canvas.height);
		renderer.begin();
		renderer.drawTexture(this.logo, (canvas.width - logoWidth) / 2, (canvas.height - logoHeight) / 2, logoWidth, logoHeight, tempColor);
		if (this.spinner) renderer.drawTextureRotated(this.spinner, (canvas.width - spinnerSize) / 2, (canvas.height - spinnerSize) / 2, spinnerSize, spinnerSize, spinnerSize / 2, spinnerSize / 2, this.angle, tempColor);
		renderer.end();
	}

	drawInCoordinates (x: number, y: number) {
		if (loaded < 2) return;

		this.timeKeeper.update();
		let renderer = this.renderer;
		renderer.batcher.setBlendMode(BlendMode.Normal, true);

		if (!this.logo) {
			this.logo = new GLTexture(renderer.context, logoImage);
			this.spinner = new GLTexture(renderer.context, spinnerImage);
		}

		const shiftedX = x - logoWidth / 2;
		const shiftedY = y - logoHeight / 2;
		renderer.drawTexture(this.logo, shiftedX, shiftedY, logoWidth, logoHeight);

		this.angle -= this.timeKeeper.delta * 500;
		if (this.spinner) renderer.drawTextureRotated(this.spinner, shiftedX, shiftedY - 25, spinnerSize, spinnerSize, spinnerSize / 2, spinnerSize / 2, this.angle);
	}
}

let SPINNER_DATA = "";

let SPINE_LOGO_DATA = "";
